﻿using System;
using System.Text;
using System.IO.Ports;

using log4net.Core;
using log4net.Appender;

namespace PowerSoft.log4net.Appender
{
    /// <summary>
    /// This class describes a log4net appender that can write to serial ports.
    /// </summary>
    public class COMAppender : AppenderSkeleton
    {
        #region Public Instance Constructors

        public COMAppender()
        {
            m_port = -1;
        }

        #endregion

        #region Publish Instance Properties

        public Encoding Encoding
        {
            get { return m_encoding; }
            set { m_encoding = value; }
        }

        public int PortNumber
        {
            get 
            { 
                if (m_port < 0)
                    throw new ArgumentNullException("COM port number not defined");
                return m_port;
            }
            set 
            {
                if (value < 0)
                    throw new ArgumentOutOfRangeException("COM port number must be 0 or greater");
                m_port = value; 
            }
        }

        public string Port
        {
            get
            {
                if (m_port < 0)
                    throw new ArgumentNullException("COM port number not defined");
                else if (m_port < 10)
                    return "COM" + m_port.ToString();
                else
                    return @"$device\COM" + m_port.ToString();
            }
        }

        public int BaudRate
        {
            get { return m_baudRate; }
            set { m_baudRate = value; }
        }

        #endregion

        #region Implementation of IOptionHandler

        public override void ActivateOptions()
        {
            base.ActivateOptions();

            InitializeClientConnection();
        }
        #endregion

        #region Override implementation of AppenderSkeleton

        protected override void Append(LoggingEvent loggingEvent)
        {
            try
            {
                byte[] buffer = m_encoding.GetBytes(RenderLoggingEvent(loggingEvent).ToCharArray());
                m_serialPort.Write(buffer, 0, buffer.Length);
            }
            catch (Exception ex)
            {
                ErrorHandler.Error("Unable to send logging event to serial port " + Port + ".",
                    ex,
                    ErrorCode.WriteFailure);
            }

        }

        protected override bool RequiresLayout
        {
            get { return true; }
        }

        protected override void OnClose()
        {
            base.OnClose();
            if (m_serialPort != null)
            {
                m_serialPort.Close();
                m_serialPort.Dispose();
                m_serialPort = null;
            }
        }

        #endregion

        #region Protected Instance Methods

        protected virtual void InitializeClientConnection()
        {
            try
            {
                m_serialPort = new SerialPort();
                m_serialPort.BaudRate = m_baudRate;
                m_serialPort.DataBits = m_dataBits;
                m_serialPort.Parity = m_parity;
                m_serialPort.PortName = Port;
                m_serialPort.ReadBufferSize = 1;
                m_serialPort.ReadTimeout = 1000;
                m_serialPort.ReceivedBytesThreshold = 1;
                m_serialPort.StopBits = m_stopBits;
                m_serialPort.WriteBufferSize = 1000;
                m_serialPort.WriteTimeout = 1000;

                m_serialPort.Open();
            }
            catch (Exception ex)
            {
                ErrorHandler.Error(
                    "Could not initialize the serial connection on port " + Port + ".",
                    ex,
                    ErrorCode.FileOpenFailure);
                this.m_serialPort.Dispose();
                this.m_serialPort = null;
            }
        }
        #endregion

        #region Private Instance Fields

        private SerialPort m_serialPort;
        private int m_baudRate;
        private int m_port;
        private Encoding m_encoding = Encoding.Default;
        private int m_dataBits = 8;
        private Parity m_parity = Parity.None;
        private StopBits m_stopBits = StopBits.One;

        #endregion
    }
}
